
#include "Vector4.h"
#include "RMatrix.h"

using namespace Riccsson::Xna::Framework;

Vector4 g_Vector4_zeroVector = Vector4();
Vector4 g_Vector4_unitVector = Vector4(1.0f, 1.0f, 1.0f, 1.0f);
Vector4 g_Vector4_unitXVector = Vector4(1.0f, 0.0f, 0.0f, 0.0f);
Vector4 g_Vector4_unitYVector = Vector4(0.0f, 1.0f, 0.0f, 0.0f);
Vector4 g_Vector4_unitZVector = Vector4(0.0f, 0.0f, 1.0f, 0.0f);
Vector4 g_Vector4_unitWVector = Vector4(0.0f, 0.0f, 0.0f, 1.0f);

const Vector4& Vector4::zeroVector  = g_Vector4_zeroVector;
const Vector4& Vector4::unitVector  = g_Vector4_unitVector;
const Vector4& Vector4::unitXVector = g_Vector4_unitXVector;
const Vector4& Vector4::unitYVector = g_Vector4_unitYVector;
const Vector4& Vector4::unitZVector = g_Vector4_unitZVector;
const Vector4& Vector4::unitWVector = g_Vector4_unitWVector;


Vector4::Vector4()
{
    this->X = 0;
    this->Y = 0;
    this->Z = 0;
    this->W = 0;
}

Vector4::Vector4(float x, float y, float z, float w)
{
    this->X = x;
    this->Y = y;
    this->Z = z;
    this->W = w;
}

Vector4::Vector4(Vector2 value, float z, float w)
{
    this->X = value.X;
    this->Y = value.Y;
    this->Z = z;
    this->W = w;
}

Vector4::Vector4(Vector3 value, float w)
{
    this->X = value.X;
    this->Y = value.Y;
    this->Z = value.Z;
    this->W = w;
}

Vector4::Vector4(float value)
{
    this->X = value;
    this->Y = value;
    this->Z = value;
    this->W = value;
}

#pragma endregion


#pragma region public: Methods

Vector4 Vector4::Add(Vector4 value1, Vector4 value2)
{
    value1.W += value2.W;
    value1.X += value2.X;
    value1.Y += value2.Y;
    value1.Z += value2.Z;
    return value1;
}

void Vector4::Add(Vector4 ref value1, Vector4 ref value2, Vector4 out result)
{
    result.W = value1.W + value2.W;
    result.X = value1.X + value2.X;
    result.Y = value1.Y + value2.Y;
    result.Z = value1.Z + value2.Z;
}

Vector4 Vector4::Barycentric(Vector4 value1, Vector4 value2, Vector4 value3, float amount1, float amount2)
{
#if(USE_FARSEER)
    return Vector4(
        SilverSpriteMathHelper::Barycentric(value1.X, value2.X, value3.X, amount1, amount2),
        SilverSpriteMathHelper::Barycentric(value1.Y, value2.Y, value3.Y, amount1, amount2),
        SilverSpriteMathHelper::Barycentric(value1.Z, value2.Z, value3.Z, amount1, amount2),
        SilverSpriteMathHelper::Barycentric(value1.W, value2.W, value3.W, amount1, amount2));
#else
    return Vector4(
        MathHelper::Barycentric(value1.X, value2.X, value3.X, amount1, amount2),
        MathHelper::Barycentric(value1.Y, value2.Y, value3.Y, amount1, amount2),
        MathHelper::Barycentric(value1.Z, value2.Z, value3.Z, amount1, amount2),
        MathHelper::Barycentric(value1.W, value2.W, value3.W, amount1, amount2));
#endif
}

void Vector4::Barycentric(Vector4 ref value1, Vector4 ref value2, Vector4 ref value3, float amount1, float amount2, Vector4 out result)
{
#if(USE_FARSEER)
    result = Vector4(
        SilverSpriteMathHelper::Barycentric(value1.X, value2.X, value3.X, amount1, amount2),
        SilverSpriteMathHelper::Barycentric(value1.Y, value2.Y, value3.Y, amount1, amount2),
        SilverSpriteMathHelper::Barycentric(value1.Z, value2.Z, value3.Z, amount1, amount2),
        SilverSpriteMathHelper::Barycentric(value1.W, value2.W, value3.W, amount1, amount2));
#else
    result = Vector4(
        MathHelper::Barycentric(value1.X, value2.X, value3.X, amount1, amount2),
        MathHelper::Barycentric(value1.Y, value2.Y, value3.Y, amount1, amount2),
        MathHelper::Barycentric(value1.Z, value2.Z, value3.Z, amount1, amount2),
        MathHelper::Barycentric(value1.W, value2.W, value3.W, amount1, amount2));
#endif
}

Vector4 Vector4::CatmullRom(Vector4 value1, Vector4 value2, Vector4 value3, Vector4 value4, float amount)
{
#if(USE_FARSEER)
    return Vector4(
        SilverSpriteMathHelper::CatmullRom(value1.X, value2.X, value3.X, value4.X, amount),
        SilverSpriteMathHelper::CatmullRom(value1.Y, value2.Y, value3.Y, value4.Y, amount),
        SilverSpriteMathHelper::CatmullRom(value1.Z, value2.Z, value3.Z, value4.Z, amount),
        SilverSpriteMathHelper::CatmullRom(value1.W, value2.W, value3.W, value4.W, amount));
#else
    return Vector4(
        MathHelper::CatmullRom(value1.X, value2.X, value3.X, value4.X, amount),
        MathHelper::CatmullRom(value1.Y, value2.Y, value3.Y, value4.Y, amount),
        MathHelper::CatmullRom(value1.Z, value2.Z, value3.Z, value4.Z, amount),
        MathHelper::CatmullRom(value1.W, value2.W, value3.W, value4.W, amount));
#endif
}

void Vector4::CatmullRom(Vector4 ref value1, Vector4 ref value2, Vector4 ref value3, Vector4 ref value4, float amount, Vector4 out result)
{
#if(USE_FARSEER)
    result = Vector4(
        SilverSpriteMathHelper::CatmullRom(value1.X, value2.X, value3.X, value4.X, amount),
        SilverSpriteMathHelper::CatmullRom(value1.Y, value2.Y, value3.Y, value4.Y, amount),
        SilverSpriteMathHelper::CatmullRom(value1.Z, value2.Z, value3.Z, value4.Z, amount),
        SilverSpriteMathHelper::CatmullRom(value1.W, value2.W, value3.W, value4.W, amount));
#else
    result = Vector4(
        MathHelper::CatmullRom(value1.X, value2.X, value3.X, value4.X, amount),
        MathHelper::CatmullRom(value1.Y, value2.Y, value3.Y, value4.Y, amount),
        MathHelper::CatmullRom(value1.Z, value2.Z, value3.Z, value4.Z, amount),
        MathHelper::CatmullRom(value1.W, value2.W, value3.W, value4.W, amount));
#endif
}

Vector4 Vector4::Clamp(Vector4 value1, Vector4 min, Vector4 max)
{
    return Vector4(
        MathHelper::ClampF(value1.X, min.X, max.X),
        MathHelper::ClampF(value1.Y, min.Y, max.Y),
        MathHelper::ClampF(value1.Z, min.Z, max.Z),
        MathHelper::ClampF(value1.W, min.W, max.W));
}

void Vector4::Clamp(Vector4 ref value1, Vector4 ref min, Vector4 ref max, Vector4 out result)
{
    result = Vector4(
        MathHelper::ClampF(value1.X, min.X, max.X),
        MathHelper::ClampF(value1.Y, min.Y, max.Y),
        MathHelper::ClampF(value1.Z, min.Z, max.Z),
        MathHelper::ClampF(value1.W, min.W, max.W));
}

float Vector4::Distance(Vector4 value1, Vector4 value2)
{
    return (float)Riccsson::System::Math::Sqrt(DistanceSquared(value1, value2));
}

void Vector4::Distance(Vector4 ref value1, Vector4 ref value2, float out result)
{
    result = (float)Riccsson::System::Math::Sqrt(DistanceSquared(value1, value2));
}

float Vector4::DistanceSquared(Vector4 value1, Vector4 value2)
{
    float result;
    DistanceSquared(/*ref*/ value1, /*ref*/ value2, /*out*/ result);
    return result;
}

void Vector4::DistanceSquared(Vector4 ref value1, Vector4 ref value2, float out result)
{
    result = (value1.W - value2.W) * (value1.W - value2.W) +
                (value1.X - value2.X) * (value1.X - value2.X) +
                (value1.Y - value2.Y) * (value1.Y - value2.Y) +
                (value1.Z - value2.Z) * (value1.Z - value2.Z);
}

Vector4 Vector4::Divide(Vector4 value1, Vector4 value2)
{
    value1.W /= value2.W;
    value1.X /= value2.X;
    value1.Y /= value2.Y;
    value1.Z /= value2.Z;
    return value1;
}

Vector4 Vector4::Divide(Vector4 value1, float divider)
{
    float factor = 1.0f / divider;
    value1.W *= factor;
    value1.X *= factor;
    value1.Y *= factor;
    value1.Z *= factor;
    return value1;
}

void Vector4::Divide(Vector4 ref value1, float divider, Vector4 out result)
{
    float factor = 1.0f / divider;
    result.W = value1.W * factor;
    result.X = value1.X * factor;
    result.Y = value1.Y * factor;
    result.Z = value1.Z * factor;
}

void Vector4::Divide(Vector4 ref value1, Vector4 ref value2, Vector4 out result)
{
    result.W = value1.W / value2.W;
    result.X = value1.X / value2.X;
    result.Y = value1.Y / value2.Y;
    result.Z = value1.Z / value2.Z;
}

float Vector4::Dot(Vector4 vector1, Vector4 vector2)
{
    return vector1.X * vector2.X + vector1.Y * vector2.Y + vector1.Z * vector2.Z + vector1.W * vector2.W;
}

void Vector4::Dot(Vector4 ref vector1, Vector4 ref vector2, float out result)
{
    result = vector1.X * vector2.X + vector1.Y * vector2.Y + vector1.Z * vector2.Z + vector1.W * vector2.W;
}

bool Vector4::Equals(const Riccsson::System::object* obj) const
{
    return ( instanceof_const(obj, Vector4) ) ? this == (Vector4*)obj : false;
}

bool Vector4::Equals(const Vector4* other) const
{
    return this->W == other->W
        && this->X == other->X
        && this->Y == other->Y
        && this->Z == other->Z;
}

int Vector4::GetHashCode()
{
    return (int)(this->W + this->X + this->Y + this->Y);
}

Vector4 Vector4::Hermite(Vector4 value1, Vector4 tangent1, Vector4 value2, Vector4 tangent2, float amount)
{
    Vector4 result = Vector4();
    Hermite(/*ref*/ value1, /*ref*/ tangent1, /*ref*/ value2, /*ref*/ tangent2, amount, /*out*/ result);
    return result;
}

void Vector4::Hermite(Vector4 ref value1, Vector4 ref tangent1, Vector4 ref value2, Vector4 ref tangent2, float amount, Vector4 out result)
{
#if(USE_FARSEER)
    result.W = SilverSpriteMathHelper::Hermite(value1.W, tangent1.W, value2.W, tangent2.W, amount);
    result.X = SilverSpriteMathHelper::Hermite(value1.X, tangent1.X, value2.X, tangent2.X, amount);
    result.Y = SilverSpriteMathHelper::Hermite(value1.Y, tangent1.Y, value2.Y, tangent2.Y, amount);
    result.Z = SilverSpriteMathHelper::Hermite(value1.Z, tangent1.Z, value2.Z, tangent2.Z, amount);
#else
    result.W = MathHelper::Hermite(value1.W, tangent1.W, value2.W, tangent2.W, amount);
    result.X = MathHelper::Hermite(value1.X, tangent1.X, value2.X, tangent2.X, amount);
    result.Y = MathHelper::Hermite(value1.Y, tangent1.Y, value2.Y, tangent2.Y, amount);
    result.Z = MathHelper::Hermite(value1.Z, tangent1.Z, value2.Z, tangent2.Z, amount);
#endif
}

float Vector4::Length()
{
    //float result;
    //Vector4::DistanceSquared(/*ref*/ *this, /*ref*/ zeroVector, /*out*/ result);
    //return (float)Riccsson::System::Math::Sqrt(result);
    throw;
}

float Vector4::LengthSquared()
{   
    //float result;
    //Vector4::DistanceSquared(/*ref*/ *this, /*ref*/ zeroVector, /*out*/ result);
    //return result;
    throw;
}

Vector4 Vector4::Lerp(Vector4 value1, Vector4 value2, float amount)
{
    return Vector4(
        MathHelper::Lerp(value1.X, value2.X, amount),
        MathHelper::Lerp(value1.Y, value2.Y, amount),
        MathHelper::Lerp(value1.Z, value2.Z, amount),
        MathHelper::Lerp(value1.W, value2.W, amount));
}

void Vector4::Lerp(Vector4 ref value1, Vector4 ref value2, float amount, Vector4 out result)
{
    result = Vector4(
        MathHelper::Lerp(value1.X, value2.X, amount),
        MathHelper::Lerp(value1.Y, value2.Y, amount),
        MathHelper::Lerp(value1.Z, value2.Z, amount),
        MathHelper::Lerp(value1.W, value2.W, amount));
}

Vector4 Vector4::Max(Vector4 value1, Vector4 value2)
{
    return Vector4(
        MathHelper::Max(value1.X, value2.X),
        MathHelper::Max(value1.Y, value2.Y),
        MathHelper::Max(value1.Z, value2.Z),
        MathHelper::Max(value1.W, value2.W));
}

void Vector4::Max(Vector4 ref value1, Vector4 ref value2, Vector4 out result)
{
    result = Vector4(
        MathHelper::Max(value1.X, value2.X),
        MathHelper::Max(value1.Y, value2.Y),
        MathHelper::Max(value1.Z, value2.Z),
        MathHelper::Max(value1.W, value2.W));
}

Vector4 Vector4::Min(Vector4 value1, Vector4 value2)
{
    return Vector4(
        MathHelper::Min(value1.X, value2.X),
        MathHelper::Min(value1.Y, value2.Y),
        MathHelper::Min(value1.Z, value2.Z),
        MathHelper::Min(value1.W, value2.W));
}

void Vector4::Min(Vector4 ref value1, Vector4 ref value2, Vector4 out result)
{
    result = Vector4(
        MathHelper::Min(value1.X, value2.X),
        MathHelper::Min(value1.Y, value2.Y),
        MathHelper::Min(value1.Z, value2.Z),
        MathHelper::Min(value1.W, value2.W));
}

Vector4 Vector4::Multiply(Vector4 value1, Vector4 value2)
{
    value1.W *= value2.W;
    value1.X *= value2.X;
    value1.Y *= value2.Y;
    value1.Z *= value2.Z;
    return value1;
}

Vector4 Vector4::Multiply(Vector4 value1, float scaleFactor)
{
    value1.W *= scaleFactor;
    value1.X *= scaleFactor;
    value1.Y *= scaleFactor;
    value1.Z *= scaleFactor;
    return value1;
}

void Vector4::Multiply(Vector4 ref value1, float scaleFactor, Vector4 out result)
{
    result.W = value1.W * scaleFactor;
    result.X = value1.X * scaleFactor;
    result.Y = value1.Y * scaleFactor;
    result.Z = value1.Z * scaleFactor;
}

void Vector4::Multiply(Vector4 ref value1, Vector4 ref value2, Vector4 out result)
{
    result.W = value1.W * value2.W;
    result.X = value1.X * value2.X;
    result.Y = value1.Y * value2.Y;
    result.Z = value1.Z * value2.Z;
}

Vector4 Vector4::Negate(Vector4 value)
{
    value = Vector4(-value.X, -value.Y, -value.Z, -value.W);
    return value;
}

void Vector4::Negate(Vector4 ref value, Vector4 out result)
{
    result = Vector4(-value.X, -value.Y, -value.Z,-value.W);
}
                        
Vector4 Vector4::Normalize(Vector4 vector)
{
    Normalize(/*ref*/ vector, /*out*/ vector);
    return vector;
}

void Vector4::Normalize(Vector4 ref vector, Vector4 out result)
{
    //float factor;
    //DistanceSquared(/*ref*/ vector, /*ref*/ zeroVector, /*out*/ factor);
    //factor = 1.0f / (float)Riccsson::System::Math::Sqrt(factor);
    //
    //result.W = vector.W * factor;
    //result.X = vector.X * factor;
    //result.Y = vector.Y * factor;
    //result.Z = vector.Z * factor;
    throw;
}

void Vector4::Normalize()
{
    Vector4::Normalize(/*ref*/ *this, /*out*/ *this);
}

Vector4 Vector4::SmoothStep(Vector4 value1, Vector4 value2, float amount)
{
#if(USE_FARSEER)
    return Vector4(
        SilverSpriteMathHelper::SmoothStep(value1.X, value2.X, amount),
        SilverSpriteMathHelper::SmoothStep(value1.Y, value2.Y, amount),
        SilverSpriteMathHelper::SmoothStep(value1.Z, value2.Z, amount),
        SilverSpriteMathHelper::SmoothStep(value1.W, value2.W, amount));
#else
    return Vector4(
        MathHelper::SmoothStep(value1.X, value2.X, amount),
        MathHelper::SmoothStep(value1.Y, value2.Y, amount),
        MathHelper::SmoothStep(value1.Z, value2.Z, amount),
        MathHelper::SmoothStep(value1.W, value2.W, amount));
#endif
}

void Vector4::SmoothStep(Vector4 ref value1, Vector4 ref value2, float amount, Vector4 out result)
{
#if(USE_FARSEER)
    result = Vector4(
        SilverSpriteMathHelper::SmoothStep(value1.X, value2.X, amount),
        SilverSpriteMathHelper::SmoothStep(value1.Y, value2.Y, amount),
        SilverSpriteMathHelper::SmoothStep(value1.Z, value2.Z, amount),
        SilverSpriteMathHelper::SmoothStep(value1.W, value2.W, amount));
#else
    result = Vector4(
        MathHelper::SmoothStep(value1.X, value2.X, amount),
        MathHelper::SmoothStep(value1.Y, value2.Y, amount),
        MathHelper::SmoothStep(value1.Z, value2.Z, amount),
        MathHelper::SmoothStep(value1.W, value2.W, amount));
#endif
}

Vector4 Vector4::Subtract(Vector4 value1, Vector4 value2)
{
    value1.W -= value2.W;
    value1.X -= value2.X;
    value1.Y -= value2.Y;
    value1.Z -= value2.Z;
    return value1;
}

void Vector4::Subtract(Vector4 ref value1, Vector4 ref value2, Vector4 out result)
{
    result.W = value1.W - value2.W;
    result.X = value1.X - value2.X;
    result.Y = value1.Y - value2.Y;
    result.Z = value1.Z - value2.Z;
}

Vector4 Vector4::Transform(Vector2 position, Matrix& matrix)
{
    Vector4 result;
    Transform(/*ref*/ position, /*ref*/ matrix, /*out*/ result);
    return result;
}

Vector4 Vector4::Transform(Vector3 position, Matrix& matrix)
{
    Vector4 result;
    Transform(/*ref*/ position, /*ref*/ matrix, /*out*/ result);
    return result;
}

Vector4 Vector4::Transform(Vector4 vector, Matrix& matrix)
{
    Transform(/*ref*/ vector, /*ref*/ matrix, /*out*/ vector);
    return vector;
}

void Vector4::Transform(Vector2 ref position, Matrix ref matrix, Vector4 out result)
{
    result = Vector4((position.X * matrix.M11) + (position.Y * matrix.M21) + matrix.M41,
                            (position.X * matrix.M12) + (position.Y * matrix.M22) + matrix.M42,
                            (position.X * matrix.M13) + (position.Y * matrix.M23) + matrix.M43,
                            (position.X * matrix.M14) + (position.Y * matrix.M24) + matrix.M44);
}

void Vector4::Transform(Vector3 ref position, Matrix ref matrix, Vector4 out result)
{
    result = Vector4((position.X * matrix.M11) + (position.Y * matrix.M21) + (position.Z * matrix.M31) + matrix.M41,
                            (position.X * matrix.M12) + (position.Y * matrix.M22) + (position.Z * matrix.M32) + matrix.M42,
                            (position.X * matrix.M13) + (position.Y * matrix.M23) + (position.Z * matrix.M33) + matrix.M43,
                            (position.X * matrix.M14) + (position.Y * matrix.M24) + (position.Z * matrix.M34) + matrix.M44);
}

void Vector4::Transform(Vector4 ref vector, Matrix ref matrix, Vector4 out result)
{
    result = Vector4((vector.X * matrix.M11) + (vector.Y * matrix.M21) + (vector.Z * matrix.M31) + (vector.W * matrix.M41),
                            (vector.X * matrix.M12) + (vector.Y * matrix.M22) + (vector.Z * matrix.M32) + (vector.W * matrix.M42),
                            (vector.X * matrix.M13) + (vector.Y * matrix.M23) + (vector.Z * matrix.M33) + (vector.W * matrix.M43),
                            (vector.X * matrix.M14) + (vector.Y * matrix.M24) + (vector.Z * matrix.M34) + (vector.W * matrix.M44));
}

GC_PTR<Riccsson::System::string> Vector4::ToString() const
{
    //StringBuilder sb = new StringBuilder(32);
    //sb.Append("{X:");
    //sb.Append(this->X);
    //sb.Append(" Y:");
    //sb.Append(this->Y);
    //sb.Append(" Z:");
    //sb.Append(this->Z);
    //sb.Append(" W:");
    //sb.Append(this->W);
    //sb.Append("}");
    //return sb.ToString();
    throw;
}

#pragma endregion public: Methods



//Vector4 operator -(Vector4 value)
//{
//    return Vector4(-value.X, -value.Y, -value.Z, -value.W);
//}
//
//bool operator ==(const Vector4& value1,const Vector4& value2)
//{
//    return value1.W == value2.W
//        && value1.X == value2.X
//        && value1.Y == value2.Y
//        && value1.Z == value2.Z;
//}
//
//bool operator !=(Vector4 value1, Vector4 value2)
//{
//    return !(value1.W == value2.W
//        && value1.X == value2.X
//        && value1.Y == value2.Y
//        && value1.Z == value2.Z);
//}
//
//Vector4 operator +(Vector4 value1, Vector4 value2)
//{
//    value1.W += value2.W;
//    value1.X += value2.X;
//    value1.Y += value2.Y;
//    value1.Z += value2.Z;
//    return value1;
//}
//
//Vector4 operator -(Vector4 value1, Vector4 value2)
//{
//    value1.W -= value2.W;
//    value1.X -= value2.X;
//    value1.Y -= value2.Y;
//    value1.Z -= value2.Z;
//    return value1;
//}
//
//Vector4 operator *(Vector4 value1, Vector4 value2)
//{
//    value1.W *= value2.W;
//    value1.X *= value2.X;
//    value1.Y *= value2.Y;
//    value1.Z *= value2.Z;
//    return value1;
//}
//
//Vector4 operator *(Vector4 value1, float scaleFactor)
//{
//    value1.W *= scaleFactor;
//    value1.X *= scaleFactor;
//    value1.Y *= scaleFactor;
//    value1.Z *= scaleFactor;
//    return value1;
//}
//
//Vector4 operator *(float scaleFactor, Vector4 value1)
//{
//    value1.W *= scaleFactor;
//    value1.X *= scaleFactor;
//    value1.Y *= scaleFactor;
//    value1.Z *= scaleFactor;
//    return value1;
//}
//
//Vector4 operator /(Vector4 value1, Vector4 value2)
//{
//    value1.W /= value2.W;
//    value1.X /= value2.X;
//    value1.Y /= value2.Y;
//    value1.Z /= value2.Z;
//    return value1;
//}
//
//Vector4 operator /(Vector4 value1, float divider)
//{
//    float factor = 1.0f / divider;
//    value1.W *= factor;
//    value1.X *= factor;
//    value1.Y *= factor;
//    value1.Z *= factor;
//    return value1;
//}